
package javaxml;

/*!
    * \file      ReportXML.java
    * \author    Tony
    * \version   1.0
    * \date      2010 - 2011
    *
 */

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.*;
import org.jdom2.output.*;
import org.jdom2.filter.*;
import org.jdom2.JDOMException;
import org.jdom2.xpath.XPath;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/*! 
    *  \class     ReportXML
    *  \brief     Manipulation des fichiers XML.
    *
    *  \details   Cette classe est bâti sur l'API JDOM, qui facilite la manipulation des métadonnées XML.
    * 
*/

public class ReportXML {
    
    protected Element racine;
    protected Element courant;
    protected Document document;
    private int index;
    
    /*!
    *  \brief Constructeur par défaut de la classe ReportXML
    */
    public ReportXML() {
            //Initialisation
            index = 0;
    }
    /*!
    *  \brief Constructeur de la classe ReportXML
    */
    public ReportXML(Element p_root) {
        racine = p_root;
        document = new Document(racine);
        //Initialisation
        index = 0;
    }
    
    /*!
    *  \brief Constructeur de la classe ReportXML
    */
    public ReportXML(String p_root) {      
    //Création de la racine XML.
    racine =new Element(p_root);
    //Création d'un nouveau Document JDOM basé sur la racine
    document = new Document(racine);
    //Initialisation
    index = 0;
    }
    
    /*!
    *  \brief Constructeur par recopie de la classe ReportXML
    */
    public ReportXML(ReportXML p_recopie) {      
    racine = p_recopie.racine;
    document = p_recopie.document;     
    index = p_recopie.index;
    }
    
    /*!
    *  \brief La méthode renvoie true ou false 
    *  verifie si le schema XML n'est pas respecté  
    * 
    *  \param[in] String p_filexsd, String p_filexml
    *  \retval <return boolean²> <br>  
    */
    public void CheckXML(String p_filexsd, String p_filexml) throws ReportException
    {
        try
        {
            SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema") ;
            InputSource sourceentree = new InputSource(new FileInputStream(new File(p_filexsd)));
            SAXSource sourceXSD = new SAXSource(sourceentree);
            Schema schema = factory.newSchema(sourceXSD);
            Validator validator = schema.newValidator() ;
            validator.validate(new StreamSource(new File(p_filexml))) ;
        } catch (SAXException e) {          
            throw new ReportException (e, "Erreur SAXException : schema XML non respecté");
        } catch (IOException e) {
            throw new ReportException( e, e);
        }
        
    }
     
    /*!
    *  \brief La méthode crée un parser pour lire le fichier XML
    * 
    *  \param[in] String
    */ 
    public void lireFichierXml(String fichier) throws ReportException
    {
        // Création d’un parser SAX
        SAXBuilder sxb = new SAXBuilder();
        try {
            // Instanciation du document correspondant
            document = sxb.build(new File(fichier));
        } catch (JDOMException e) {
            throw new ReportException( e, e);
        } catch (FileNotFoundException e) {
            throw new ReportException( e, "Le fichier n’a pas été trouvé");
        } catch (IOException e) {
            throw new ReportException( e, e);
        }
 
        racine = document.getRootElement();
    }
    
    /*!
    *  \brief La méthode renvoie un tableau dynamique de tous les éléments  
    *  constituant le fichier XML
    * 
    *  \param[in] Element element, int niveau
    *  \retval <return ArrayList<String>> <br>  
    */
    public ArrayList<String> afficherFils(Element p_element, int p_niveau)
    {
        ArrayList<String> arrayResult = new ArrayList<String>();
        afficherFils(p_element, arrayResult, p_niveau);
        return arrayResult;
    }
    
    /*!
    *  \brief Surcharge de la méthode afficherFils.  
    * 
    *  \param[in] Element, ArrayList<String>, int
    *
    */
    private void afficherFils(Element element, ArrayList<String> arrayResult, int niveau){
        element.setAttribute("Id", String.valueOf(index));
        String parent = "root";
           if (element != racine) {
               parent = element.getParentElement().getName();
           }
        System.out.println(element.getName() + "=" + element.getText() + "=");
        arrayResult.add (element.getName() + "=" + element.getText() + "=" + parent + "=" + element.getAttributeValue("Id") );
        
        List fils = element.getChildren();
        Iterator iterator = fils.iterator();
        while (iterator.hasNext()) {
            index = index + 1;
            Element elementFils = (Element) iterator.next();
            afficherFils(elementFils, arrayResult, niveau+1);
        }
    }
    
    /*!
    *  \brief La méthode renvoie un tableau dynamique de tous les éléments  
    *  correspondant à la balise fournis en paramètre.
    * 
    *  \param[in] Element , String
    *  \retval <return ArrayList<String>> <br> 
    */
    public ArrayList<String> afficherFiltre(Element element, String item)
    {
        ArrayList<String> arrayResult = new ArrayList<String>();
        afficherFiltre(element, arrayResult, item);
        return arrayResult;
    }
    
    /*!
    *  \brief Surcharge de la méthode afficherFiltre
    *
    *  \param[in] Element, ArrayList<String>, String
    */
    private void afficherFiltre(Element element, ArrayList<String> arrayResult, String item) 
    {   
        ElementFilter filtre = new ElementFilter(item); 
        
        List childrenFiltre = element.getContent(filtre); 
        Iterator iterator = childrenFiltre.iterator(); 
 
        while (iterator.hasNext()) {
            Element elementfiltre = (Element) iterator.next(); 
            
            arrayResult.add (elementfiltre.getName() + "=" + elementfiltre.getText() + "=" + elementfiltre.getParentElement().getName() + "=" + elementfiltre.getAttributeValue("Id") );
            //System.out.println(elementfiltre.getName() + " = " + elementfiltre.getAttributeValue("Id"));
        }
        
        List fils = element.getChildren();
        Iterator i = fils.iterator();
        while (i.hasNext()) {
            Element verifSuivant = (Element) i.next();
            afficherFiltre(verifSuivant, arrayResult, item);
        }        
    }
    
    /*!
    *  \brief La méthode affiche le path de l'élément fournis en paramétre.
    * 
    *  \param[in] Element, String, String
    */
    public void afficherElement(Element element, String nom, String attribid) throws ReportException{
        try {                     
            // Recherche de la liste des link      
            XPath xpa = XPath.newInstance("//item/link");   
            System.out.println("Valeur : " + xpa.getXPath());
            // On récupère tous les noeuds répondant au chemin
            List results = xpa.selectNodes(racine) ;
            
            Iterator i = results.iterator() ;

            String Id = null ;
            while (i.hasNext()){
 
                Element noeudCourant = (Element) i.next();
                //Element courant = (Element) xpa.selectSingleNode(noeudCourant);
                 
                //xpa = XPath.newInstance("./@Id");
                Id = xpa.valueOf(noeudCourant);
                
               // xpa = XPath.newInstance("//personne[@id='" + Id + "']");
               // noeudCourant = (Element) xpa.selectSingleNode(noeudCourant);             
                System.out.println("Valeur : " + xpa.valueOf(noeudCourant) + " " + courant); 

            }
        } catch (JDOMException e) {
            throw new ReportException( e, e.getMessage());
        }
        
    }

    /*!
    *  \brief La méthode détermine un élément en fonction de l'attribut fournis en paramétre.
    * 
    *  \param[in] Element, String, String
    */
    private void rechercherId(Element element, String attrib, String value){
        try {
        if (element.getAttributeValue(attrib).equals(value)){
            System.out.println(element.getAttributeValue(attrib));
            System.out.println(element);
            courant = element;
        } else {
            List fils = element.getChildren();
            Iterator iterator = fils.iterator();
            while (iterator.hasNext()) {
                Element elementFils = (Element) iterator.next();
            rechercherId(elementFils, attrib, value);
            }
        }
        } catch (Exception e ) {
                       
        }
    }
    
     public ArrayList<HashMap<String, String>> rechercher(String path, String attribut, String value) throws ReportException { 
         
        //liste de tableaux contenant des paires
        ArrayList<HashMap<String, String>> content = new ArrayList<HashMap<String, String>>();
        //paires
        HashMap<String, String>Pairs = null;
        String attributeValue;              
            
        try {
            /* On initialise un nouvel élément avec l'élément racine du
               document. */
            racine = document.getRootElement();
            //obtention d'un itérateur sur l'élément recherché 
            Iterator it1 = racine.getDescendants(new ElementFilter(path));
            //tant qu'il y a des descendants
            while (it1.hasNext()) {
                //noeud traité
                Element e1 = (Element)it1.next();
                //obtention d'un itérateur sur les descendants
                Iterator it2 = e1.getChildren().iterator();   
                //si l'attribut demandé existe ...
                if (e1.getAttribute(attribut) != null) {
                    attributeValue = e1.getAttributeValue(attribut);
                    //... et s'il correspond à la valeur recherchée
                    if(attributeValue.equals(value)) {
                        Pairs = new HashMap<String, String>();
                        //pour tous les éléments
                        while(it2.hasNext()) {
                            Element e2 = (Element)it2.next();
                            //ajout du nom et de la valeur du noeud
                            Pairs.put(e2.getName(), e2.getValue());
                        }
                        //ajout des paires dans la liste de tableaux
                        content.add(Pairs);
                    }
                }
            }                       
        } catch (Exception e ) {
             throw new ReportException( e, e.getMessage());           
        }
        return content;    
      }
    

   
    /*!
    *  \brief La méthode supprime le noeud fournis en paramétre.
    * 
    *  \param[in] Element, String, String
    */
    public void supprimer(Element element, String attrib, String value){
            rechercherId(element, attrib, value);
            int ind = courant.getParentElement().indexOf(courant);
            courant.getParentElement().removeContent(ind);           
    }
    
    /*!
    *  \brief La méthode supprime tous les noeuds identiques.
    * 
    *  \param[in] Element, String, String
    */
    public void supprimerNoeuds(Element element, String nom) 
    {   
        List elements = element.getChildren(nom);   
        boolean err = elements.removeAll(elements);
        if (err = true){
            System.out.println("suppr");
        } else System.out.println("erreur");
        // recherche de nouveau des fils puisque la collection à potenciellement été modifiée 
        List fils = element.getChildren(); 
        Iterator iterator = fils.iterator();   
        while (iterator.hasNext()) {     
            supprimerNoeuds((Element) iterator.next(), nom);    
        }    
    } 
    
    /*!
    *  \brief La méthode modifie le contenu d'une balise 
    * 
    *  \param[in] Element, String, String, String
    */
    public void modifier(Element element, String attrib, String value, String text)
    {   
        rechercherId(element, attrib, value);
            if(courant.getText() != null)
            {
                // modification du contenu de l'élément
                courant.setText(text);
                System.out.println("L'Element " + element + " a été modifié du fichier XML ");
            } else System.out.println("Modification impossible !!! "); 
    }
    
    /*!
    *  \brief Surcharge de la méthode modifier 
    * 
    *  \param[in] Element, int, String
    */
    public void modifier(String element, int index, String text)
    {   
        //Liste de tous les Elements
        List listItem = racine.getChildren();
        Iterator i = listItem.iterator();
    
        //Parcours de la liste grâce à un iterator
        while(i.hasNext())
        {
            courant = (Element)i.next();
            if(courant.getText()!=null)
            {
                // modification 
                courant.setText(text);
                System.out.println("L'Element " + element + " a été modifié du fichier XML ");
            } else System.out.println("Modification impossible !!! ");
            
        }
    }
    
    /*!
    *  \brief La méthode ajoute un noeud au fichier XML 
    * 
    *  \param[in] Element, String, String, String
    */
    public void ajouter(Element element, String attrib, String value, String item)
    {   
        rechercherId(element, attrib, value);
        Element Item = new Element(item);
        int ind = courant.getParentElement().indexOf(courant) + 1;
        courant.getParentElement().addContent(ind, Item);
    }
    
    /*!
    *  \brief La méthode ajoute un noeud fils au fichier XML 
    * 
    *  \param[in] Element, String, String, String
    */
    public void ajouterFils(Element element, String attrib, String value, String item)
    {
        rechercherId(element, attrib, value);
        Element Item = new Element(item);
        courant.addContent(Item);
    }
    
    /*!
    *  \brief La méthode ajoute un attribut à un noeud 
    * 
    *  \param[in] Element, String, String, String
    */
    public void attributId(Element element, String attribut, String texte){  
        element.setAttribute(attribut, texte);
    }
    
    /*!
    *  \brief La méthode supprime le attribut Id
    * 
    *  \param[in] Element
    */
    private void removeId(Element element){
        element.removeAttribute("Id");          
        List fils = element.getChildren();
        Iterator iterator = fils.iterator();
        while (iterator.hasNext()) {
            Element elementFils = (Element) iterator.next();
            removeId(elementFils);
        }
    }
    
    /*!
    *  \brief La méthode enregistre les modifications apportées au fichier XML
    * 
    *  \param[in] String
    *  \retval <return String> <br> 
    */ 
    public String enregistreFichierXml(String fichier) {   
        removeId(racine);
        try {
            //Affichage sur flux de sorti avec getPrettyFormat()
            XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
            // Création d'une instance de FileOutputStream pour effectuer la sérialisation.
            sortie.output(document, new FileOutputStream(fichier));
            System.out.println("Le fichier nommé " + fichier + " est enregistré");
        }
        catch (java.io.IOException e){
            return e.getMessage();
        }
        return fichier;
    }

    /*!
    *  \brief La méthode retourne l'élement racine
    * 
    *  \retval <return Element> <br> 
    */ 
    public Element returnRacine(){
        return racine;
    }

}